Skip to content

NoSuchMethodException when forcing the use of Log4J2LoggingSystem using org.springframework.boot.logging.LoggingSystem system property#49343

Open
FBibonne wants to merge 8 commits intospring-projects:mainfrom
FBibonne:fix-Log4J2-forced
Open

NoSuchMethodException when forcing the use of Log4J2LoggingSystem using org.springframework.boot.logging.LoggingSystem system property#49343
FBibonne wants to merge 8 commits intospring-projects:mainfrom
FBibonne:fix-Log4J2-forced

Conversation

@FBibonne
Copy link
Contributor

The Spring Boot documentation states that users can force a particular logging system by setting the system property org.springframework.boot.logging.LoggingSystem to the fully qualified class name of a LoggingSystem implementation.

When attempting to do so for Log4J2 (setting org.springframework.boot.logging.LoggingSystem = org.springframework.boot.logging.log4j2.Log4J2LoggingSystem), Spring Boot crashes with the following error (Spring Boot 4.0.3):

java.lang.NoSuchMethodException: org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.<init>(java.lang.ClassLoader)
	at java.base/java.lang.Class.getConstructor0(Class.java:3187)
	at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2491)
	at org.springframework.boot.logging.LoggingSystem.get(LoggingSystem.java:178)
	... 17 more

A different error occurs with Spring Boot 3.5.x, but this PR focuses on Spring Boot 4.

Reproduction

The issue can be reproduced by running the following main method with spring-boot-starter and spring-boot-starter-log4j2 on the classpath (with java 25):

@SpringBootApplication
public class Main {
    void main() {
        System.setProperty("org.springframework.boot.logging.LoggingSystem", "org.springframework.boot.logging.log4j2.Log4J2LoggingSystem");
        SpringApplication.run(Main.class);
    }
}

An example project is available at: https://framagit.org/FBibonne/poc-java/-/tree/spring-boot-force-log4j2 (make sure to stay on the spring-boot-force-log4j2 branch).

Root cause and fix

The error message above (java.lang.NoSuchMethodException: org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.<init>) indicates that a specific constructor is missing in Log4J2LoggingSystem : the missing constructor was removed in PR #47424. This PR proposes reintroducing it as a private constructor (to prevent unintentional direct use) since it is invoked via reflection. The constructor body reuses the code introduced by PR #47424 in org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory to obtain a loggerContext.

The test org.springframework.boot.logging.LoggingSystemTests#log4J2CanBeForced reproduces the problem: it fails if Log4J2LoggingSystem is not fixed.

Note on tests

The build of the core/spring-boot module succeeds; however, the full project build fails due to some container-based tests:

  • DataLdapTestDockerTests, OpenLdapContainerConnectionDetailsFactoryIntegrationTests, OpenLdapDockerComposeConnectionDetailsFactoryIntegrationTests: disabled because they require exposing a privileged port (389), which is not permitted on my machine.
  • PulsarDockerComposeConnectionDetailsFactoryIntegrationTests: required adding the property services.pulsar.security_opt with the value seccomp:unconfined in pulsar-compose.yaml to bypass a security restriction.
  • ZipkinContainerConnectionDetailsFactoryIntegrationTests: disabled — root cause not yet identified.

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
- fix the missing constructor in Log4J2LoggingSystem class (can be
  called by LoggingSystem.get(ClassLoader, String)

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Feb 27, 2026
@wilkinsona
Copy link
Member

Thanks for the PR.

A different error occurs with Spring Boot 3.5.x, but this PR focuses on Spring Boot 4.

Can you please share some details about the error that occurs with 3.5.x? I can't reproduce a failure with that version. Furthermore, there's a public Log4J2LoggingSystem(ClassLoader classLoader) constructor so I cannot see why an error would occur either.

@wilkinsona wilkinsona added the status: waiting-for-feedback We need additional information before we can continue label Feb 27, 2026
@FBibonne
Copy link
Contributor Author

FBibonne commented Feb 27, 2026

With Spring Boot 3.5.11 (in the project located at https://framagit.org/FBibonne/poc-java/-/tree/spring-boot-force-log4j2, I set the version of spring-boot-starter-parent at 3.5.11), I get this error if I run the poc.Main class :

Exception in thread "main" java.lang.ClassCastException: class org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to class org.apache.logging.log4j.core.LoggerContext (org.apache.logging.slf4j.SLF4JLoggerContext and org.apache.logging.log4j.core.LoggerContext are in unnamed module of loader 'app')
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.getLoggerContext(Log4J2LoggingSystem.java:497)
        at org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.beforeInitialize(Log4J2LoggingSystem.java:155)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:238)
        at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:220)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:185)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:178)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:156)
        at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
        at org.springframework.boot.context.event.EventPublishingRunListener.multicastInitialEvent(EventPublishingRunListener.java:136)
        at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:75)
        at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:54)
        at java.base/java.util.ImmutableCollections$List12.forEach(ImmutableCollections.java:681)
        at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:118)
        at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:54)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:310)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350)

It is true that the constructor of Log4J2LoggingSystem exists and is called successfully. But the exception above is raised just after (nex line in org.springframework.boot.context.logging.LoggingApplicationListener#onApplicationStartingEvent). I think that this class cast exception is what PR #47424 pointed out.

So if I exclude spring-boot-starter-logging from the dependencies, the exception above does not occur and the application does not crash. Sorry, I missed the exclusion.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Feb 27, 2026
@wilkinsona wilkinsona changed the title Error when forcing the logging system with the org.springframework.boot.logging.LoggingSystem property NoSuchMethodException when forcing the use of Log4J2LoggingSystem using org.springframework.boot.logging.LoggingSystem system property Feb 27, 2026
@wilkinsona wilkinsona added type: regression A regression from a previous release and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels Feb 27, 2026
@wilkinsona wilkinsona added this to the 4.0.x milestone Feb 27, 2026
- refactor the test for log4j
- add test for JUL

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
- code sharing between factory and constructor
- unique origin for the loggerContext generated inside the class

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
…ge private

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
…LoggerContext)

- see comment spring-projects#49343 (comment)

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
…lassLoader, LoggerContext) removal

- remove LoggerContext from LoggerContextFactory after each unit test to avoid that it is reused

Signed-off-by: Fabrice Bibonne <fabrice.bibonne@courriel.eco>
this.loggingSystem.getConfiguration().stop();
this.loggingSystem.cleanUp();
PluginRegistry.getInstance().clear();
LogManager.getFactory().removeContext(this.loggingSystem.getLoggerContext());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary?

Comment on lines -130 to +131
assertThat(configuration.getConfigurationSource().getFile()).isNotNull();
assertThat(configuration.getConfigurationSource().getLocation()).contains("log4j2-test.xml");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change appears to be unrelated. Please revert

Comment on lines -122 to +129
* Create a new {@link Log4J2LoggingSystem} instance.
* Create a new {@link Log4J2LoggingSystem} instance. The loggerContext is
* instantiated internally in the class from
* {@link LogManager#getContext(ClassLoader, boolean)} <br/>
* This constructor is also intended to be used by
* <code>LoggingSystem.get(ClassLoader, String)</code> with reflection.
* @param classLoader the class loader to use.
* @param loggerContext the {@link LoggerContext} to use.
* @throws IllegalArgumentException if the loggerContext instantiated internally is
* not of type org.apache.logging.log4j.core.LoggerContext
Copy link
Member

@wilkinsona wilkinsona Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these changes add much, particular not the part that's describing what the code does. We generally reserve comments for situations where we need to describe why something is the way it is. The code itself describes the what. Please revert. This will leave this constructor more closely aligned with the other logging system constructors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I just wonder if you have an other way that the test to figure that the constructor is called using the reflection API (seems to be important to avoid accidental removing)

@snicoll snicoll added the status: waiting-for-feedback We need additional information before we can continue label Mar 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-feedback We need additional information before we can continue type: regression A regression from a previous release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants